﻿using System;
using Ewk.Math.Algebra.Algorithms.Matrices.Adjugate;
using Ewk.Math.Algebra.UnitTests.MatrixTests;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;

namespace Ewk.Math.Algebra.UnitTests.Algorithms.Matrices
{
    [TestClass]
    public class LaplaceAdjugateFormulaTests : MatrixUnitTestsBase<double>
    {
        LaplaceAdjugateFormula<double> _computer;

        protected override void AdditionalSetup()
        {
            base.AdditionalSetup();

            _computer = new LaplaceAdjugateFormula<double>();
        }

        [TestMethod]
        public void LaplaceAdjugateFormula_ComputeAdjugate_returns_null_when_provided_matrix_is_null()
        {
            var result = _computer.ComputeAdjugate(null);

            Assert.IsNull(result);
        }

        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
        public void LaplaceAdjugateFormula_ComputeAdjugate_throws_an_InvalidOperationException_when_provided_matrix_is_not_squared()
        {
            /* Matrix 1
                    1   2   3   4   5
                    ------------------
                1 | 1   2   3   4   5
                2 | 2   4   6   8   10
                3 | 3   6   9   12  15
            */

            const int rowCount = 3;
            const int columnCount = 5;
            var matrix1 = CreateMatrix(rowCount, columnCount, (i, j) => (i + 1) * (j + 1));

            _computer.ComputeAdjugate(matrix1);
        }

        [TestMethod]
        public void LaplaceAdjugateFormula_ComputeAdjugate_computes_the_determinant_of_the_identity_matrix_3()
        {
            /* Matrix 1
                    1   2   3
                    ---------
                1 | 1   0   0
                2 | 0   1   0
                3 | 0   0   1
            */

            const int m = 3;
            var matrix1 = Matrix<double>.Identity(m);

            DeterminantComputer
                .Expect(
                    computer =>
                    computer.ComputeDeterminant(Arg<Matrix<double>>.Is.Anything))
                .Return(1)
                .Repeat.Any();
            DeterminantComputer.Replay();

            var result = _computer.ComputeAdjugate(matrix1);
            var expectedResult = CreateMatrix(m, m, (i, j) => System.Math.Pow(-1, (i + j)));
            Assert.AreEqual(expectedResult, result);
        }

        [TestMethod]
        public void LaplaceAdjugateFormula_ComputeAdjugate_computes_the_determinant_of_a_matrix_3()
        {
            /* Matrix 1
                    1   2   3
                    ---------
                1 | 1   2   3
                2 | 2   4   6
                3 | 3   6   9
            */

            const int m = 3;
            var matrix1 = CreateMatrix(m, m, (i, j) => (i + 1) * (j + 1));

            DeterminantComputer
                .Expect(
                    computer =>
                    computer.ComputeDeterminant(Arg<Matrix<double>>.Is.Anything))
                .Return(1)
                .Repeat.Any();
            DeterminantComputer.Replay();

            var result = _computer.ComputeAdjugate(matrix1);
            var expectedResult = CreateMatrix(m, m, (i, j) => System.Math.Pow(-1, (i + j)));
            Assert.AreEqual(expectedResult, result);
        }

        [TestMethod]
        public void LaplaceAdjugateFormula_ComputeAdjugate_computes_the_determinant_of_the_identity_matrix_4()
        {
            /* Matrix 1
                    1   2   3   4
                    -------------
                1 | 1   0   0   0
                2 | 0   1   0   0
                3 | 0   0   1   0
                4 | 0   0   0   1
            */

            const int m = 4;
            var matrix1 = Matrix<double>.Identity(m);

            DeterminantComputer
                .Expect(
                    computer =>
                    computer.ComputeDeterminant(Arg<Matrix<double>>.Is.Anything))
                .Return(1)
                .Repeat.Any();
            DeterminantComputer.Replay();

            var result = _computer.ComputeAdjugate(matrix1);
            var expectedResult = CreateMatrix(m, m, (i, j) => System.Math.Pow(-1, (i + j)));
            Assert.AreEqual(expectedResult, result);
        }

        [TestMethod]
        public void LaplaceAdjugateFormula_ComputeAdjugate_computes_the_determinant_of_a_matrix_4()
        {
            /* Matrix 1
                    1   2   3   4
                    -------------
                1 | 1   2   3   4
                2 | 2   4   6   8
                3 | 3   6   9   12
                4 | 4   8   12  16
            */

            const int m = 4;
            var matrix1 = CreateMatrix(m, m, (i, j) => (i + 1) * (j + 1));

            DeterminantComputer
                .Expect(
                    computer =>
                    computer.ComputeDeterminant(Arg<Matrix<double>>.Is.Anything))
                .Return(1)
                .Repeat.Any();
            DeterminantComputer.Replay();

            var result = _computer.ComputeAdjugate(matrix1);
            var expectedResult = CreateMatrix(m, m, (i, j) => System.Math.Pow(-1, (i + j)));
            Assert.AreEqual(expectedResult, result);
        }

    }
}
